package com.d4a5.mapping.google
{
	import com.adobe.serialization.json.JSON;
	import com.d4a5.mapping.core.AmfFactory;
	import com.d4a5.mapping.core.AmfObject;
	import com.d4a5.mapping.core.AmfPackage;
	import com.d4a5.mapping.core.AmfPackages;
	import com.google.maps.interfaces.IOverlay;
	import com.google.maps.interfaces.IPane;
	
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	
	import mx.core.ByteArrayAsset;
	import mx.events.FlexEvent;
	
	
	public class wmgArcgisMapServicePuller extends wmgGraphicsLayer implements IEventDispatcher
	{
		
		protected var _url:String;
		protected var _urlChanged:Boolean = false;
		protected var _amfUrl:String;
		protected var _amfUrlChanged:Boolean = false;
		
		protected var _serviceInfo:Object;
		
		protected var _puller:URLLoader;
		
		protected var _dispatcher:EventDispatcher;
		
		protected var _benchmarkingStart:Date;
		
		public function wmgArcgisMapServicePuller()
		{
			super();
			
			this._dispatcher = new EventDispatcher( this );
			
			this._puller = new URLLoader();
			this._puller.addEventListener(IOErrorEvent.IO_ERROR,onPullFailed);
			this._puller.addEventListener(ProgressEvent.PROGRESS,onProgress);
			this._puller.dataFormat = URLLoaderDataFormat.BINARY;
			
		}
		
		override public function set pane(p:IPane):void {
			super.pane = p;
			if( this._urlChanged && this._pane && this._pane.map ){
				this.startPullingOverlays();
			}
			
			if( this._amfUrlChanged && this._pane && this._pane.map ){
				this.startPullingAmfOverlays();
			}
		}
		
		public function get serviceInfo() : Object {
			return this._serviceInfo;
		}
		
		public function get url() : String {
			return this._url;
		}
		public function set url( u:String ) : void {
			if( u && u != this._url ){
				this._url = u;
				this._urlChanged = true;
				this._amfUrl = null;
				
				if( this._pane && this._pane.map ){
					this.startPullingOverlays();
				}
			}
		}
		
		
		
		public function get amfUrl() : String {
			return this._amfUrl;
		}
		public function set amfUrl( u:String ) : void {
			if( u && u != this._amfUrl ){
				this._amfUrl = u;
				this._amfUrlChanged = true;
				this._url = null;
				
				if( this._pane && this._pane.map ){
					this.startPullingAmfOverlays();
				}
			}
		} 
		
		
		protected function startPullingOverlays() : void {
			this.dispatchEvent( new FlexEvent( FlexEvent.LOADING ) );
			
			this._urlChanged = false;
			
			var gp:Array = this._graphicsProvider.toArray();
			var numGps:int = gp.length;
			for( var i:int = 0; i < numGps; i++ ){
				this.removeOverlay( gp[i] as IOverlay );
			}
			
			
			this.getServiceInfo();
		}
		
		protected function startPullingAmfOverlays() : void {
			this.dispatchEvent( new FlexEvent( FlexEvent.LOADING ) );
			
			this._amfUrlChanged = false;
			
			var gp:Array = this._graphicsProvider.toArray();
			var numGps:int = gp.length;
			for( var i:int = 0; i < numGps; i++ ){
				this.removeOverlay( gp[i] as IOverlay );
			}
			
			this._benchmarkingStart = new Date();
			this.getAmfs();
		}
		
		
		
		
		
		
		protected function getServiceInfo() : void {
			this._serviceInfo = null;
			
			if( this._url ){
				this._benchmarkingStart = new Date();
				
				var loader:URLLoader = new URLLoader();
				var request:URLRequest = new URLRequest( this._url + "?f=json" );
				
				loader.addEventListener(Event.COMPLETE,onGotServiceInfo);
				loader.addEventListener(IOErrorEvent.IO_ERROR,onFailedToGetServiceInfo);
				
				loader.load( request );
			}
		}
		protected function onFailedToGetServiceInfo( event:IOErrorEvent ) : void {
			throw new Error("ERROR : wmgArcgisDynamicMapServiceOverlay : Failed to get Service Info : from(" + this._url + ")"  );
		}
		protected function onGotServiceInfo( event:Event ) : void {
			this._serviceInfo = JSON.decode( event.target.data as String , true );
			
			if( (this._serviceInfo.capabilities as String).match( "Map" ) == false ){
				throw new Error("Error : wmgArcgisDynamicMapServiceOverlay : Service is not map capable");
			}
			
			if( this._pane && this._pane.map ){
				
				var url:String = this._url + "/query?";
				
				url += "f=amf&";
				url += "outFields=*&";
				url += "returnGeometry=true&";
				url += "text=%";
				
				this._puller.addEventListener(Event.COMPLETE,onPullComplete);
				this._puller.load( new URLRequest( url ) );
			}
		}
		
		
		protected function getAmfs() : void {
			if( this._pane && this._pane.map ){
				
				this._puller.addEventListener(Event.COMPLETE,onAmfPullComplete);
				this._puller.load( new URLRequest( this._amfUrl ) );
			}
		}
		
		
		
		protected function onPullComplete( event:Event ) : void {
			var stop:Date = new Date();
			trace( "Pull Data: (" + (stop.time - this._benchmarkingStart.time).toString() + ")" );
			this._benchmarkingStart = new Date();
			
			this._puller.removeEventListener(Event.COMPLETE,onPullComplete);
			
			
			
			var b:ByteArray = this._puller.data as ByteArray;
			var data:Object = b.readObject();
			
			var isGeographic:Boolean =  ( data.spatialReference.wkid == 4326 );
			
			
			var overlayClass:Class;
			var geomType:int = AmfFactory.typeNULL;
			var geomTypeStr:String = data.geometryType;
			if( geomTypeStr == "esriGeometryPoint" ){
				geomType = AmfFactory.typePoint;
				overlayClass = wmgMarker;
				geomTypeStr = "point";
			}
			else if( geomTypeStr == "esriGeometryPolyline" ){
				geomType = AmfFactory.typePolyline;
				overlayClass = wmgPolyline;
				geomTypeStr = "paths";
			}
			else if( geomTypeStr == "esriGeometryPolygon" ){
				geomType = AmfFactory.typePolygon;
				overlayClass = wmgPolygon;
				geomTypeStr = "rings";
			}
			else if( geomTypeStr == "esriGeometryMultipoint" ){
				//geomType = AmfFactory.typeMultipoint;
				//geomTypeStr = "points";
			}
			
			var features:Array = data.features as Array;
			var numFeatures:int = features.length;
			for( var i:int = 0; i < numFeatures; i++ ){
				var feature:Object = features[i];
				
				var attrDict:Dictionary = new Dictionary();
				var attributes:Object = feature.attributes;
				for( var key:String in attributes ){
					attrDict[key] = attributes[key];
				}
				
				var amf:AmfObject = AmfFactory.Create( AmfFactory.formatESRI , geomType , feature.geometry[geomTypeStr] , attrDict , true , isGeographic );
				this.addOverlay( new overlayClass( amf ) as IOverlay  );
			}
			
			stop = new Date();
			trace( "Read Data: (" + (stop.time - this._benchmarkingStart.time).toString() + ")" );
			
			this.dispatchEvent( new Event( "DATA_PULL_COMPLETE" ) );
		}
		
		protected function onAmfPullComplete( event:Event ) : void {
			var stop:Date = new Date();
			trace( "Pull AmfData: (" + (stop.time - this._benchmarkingStart.time).toString() + ")" );
			this._benchmarkingStart = new Date();
			
			this._puller.removeEventListener(Event.COMPLETE,onAmfPullComplete);
			
			var packages:AmfPackages = AmfFactory.UnPackage( this._puller.data as ByteArray );
			
			if( packages && packages.length == 1 ){
				var p:AmfPackage = packages.getPackage( 0 );
				
				if( p.isValid ){
					var amfs:Array = p.data as Array;
					var numAmfs:int = amfs.length;
					
					var amf:AmfObject = amfs[0] as AmfObject;
					var shapeClass:Class;
					if( amf.geometryType == AmfObject.typePoint ){
						shapeClass = wmgMarker;
					}
					else if( amf.geometryType == AmfObject.typePolyline ){
						shapeClass = wmgPolyline;
					}
					else if( amf.geometryType == AmfObject.typePolygon ){
						shapeClass = wmgPolygon;
					}
					
					
					var start:Date = new Date();
					for( var i:int = 0; i < numAmfs; i++ ){ 
						this.addOverlay( new shapeClass(amfs[i]) as IOverlay );
					}
					stop = new Date();
					trace( "AddOverlays: (" + (stop.time - start.time).toString() + ")" );
				}
			}
			
			
			stop = new Date();
			trace( "Read Data: (" + (stop.time - this._benchmarkingStart.time).toString() + ")" );
			
			this.dispatchEvent( new Event( "DATA_PULL_COMPLETE" ) );
		}
		
		
		
		protected function onPullFailed( event:IOErrorEvent ) : void {
			throw new Error( event.text );
		}
		
		
		protected function onProgress( event:ProgressEvent ) : void {
			this.dispatchEvent( new ProgressEvent( ProgressEvent.PROGRESS , event.bubbles , event.cancelable , event.bytesLoaded , event.bytesTotal ) );
		}
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void{
			this._dispatcher.addEventListener( type , listener , useCapture , priority , useWeakReference );
		}
		
		public function dispatchEvent(event:Event):Boolean{
			return this._dispatcher.dispatchEvent( event );
		}
		
		public function hasEventListener(type:String):Boolean{
			return this._dispatcher.hasEventListener( type );
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
			this._dispatcher.removeEventListener( type , listener , useCapture );
		}
		
		public function willTrigger(type:String):Boolean{
			return this._dispatcher.willTrigger( type );
		}
	}
}